package cn.com.chengmandian.bike.utils;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class AliPayUtil {
    private final static String SERVER_URL = "https://openapi.alipay.com/gateway.do";
    private final static String FORMAT = "json";
    private final static String CHARSET = "UTF-8";
    private final static String SIGN_TYPE = "RSA2";

    private final static String CREATE = "alipay_trade_create_response";
    private final static String QUERY = "alipay_trade_query_response";
    private final static String REFUND = "alipay_trade_refund_response";

    private final static String SUCCESS_CODE = "10000";

    /**
     * 获取创建请求的参数-唤起支付宝支付参数
     */
    public static Map<String, String> getTradeCreateRequestParam(
        String appId,
        String buyerId,
        String buyerOpenId,
        String subject,
        String outTradeNo,
        BigDecimal totalAmount,
        String privateKey,
        String certPath,
        String alipayPublicCertPath,
        String rootCertPath,
        String notifyUrl
    ) throws AlipayApiException {
        return getTradeCreateRequestParam(appId, buyerId, buyerOpenId, subject, outTradeNo, totalAmount, "60m", privateKey, certPath, alipayPublicCertPath, rootCertPath, notifyUrl);
    }
    /**
     * 获取创建请求的参数-唤起支付宝支付参数
     */
    public static Map<String, String> getTradeCreateRequestParam(
            String appId,
            String buyerId,
            String buyerOpenId,
            String subject,
            String outTradeNo,
            BigDecimal totalAmount,
            String timeoutExpress,
            String privateKey,
            String certPath,
            String alipayPublicCertPath,
            String rootCertPath,
            String notifyUrl
    ) throws AlipayApiException {
        AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
        request.setNotifyUrl(notifyUrl);
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo);
        bizContent.put("total_amount", totalAmount.doubleValue());
        bizContent.put("subject", subject);
        bizContent.put("product_code", "JSAPI_PAY");
        bizContent.put("op_app_id", appId);
        if(StrUtil.isNotBlank(buyerId)) {
            bizContent.put("buyer_id", buyerId);
        } else {
            bizContent.put("buyer_open_id", buyerOpenId);
        }
        bizContent.put("timeout_express", timeoutExpress);
        request.setBizContent(bizContent.toJSONString());
        AlipayTradeCreateResponse response = getAlipayClient(appId, privateKey, certPath, alipayPublicCertPath, rootCertPath).certificateExecute(request);
        if (!response.isSuccess()) {
            log.error("getAlipayClient error：{}，bizContent:{}", response.getMsg(), bizContent.toJSONString());
            throw new RuntimeException(response.getMsg());
        }
        if (!SUCCESS_CODE.equals(response.getCode())) {
            log.error("唤起支付-验签失败={}", response.getBody());
            throw new RuntimeException("唤起支付-验签失败");
        }
        Map map = JSON.parseObject(response.getBody(), Map.class);
        log.info("支付宝响应成功参数-唤起支付={}", map);
        return getMapValueToMap(map, CREATE);
    }

    /**
     * 支付宝-唤起金额退款
     * 多次退款
     * @return Map<String,String>
     * @throws AlipayApiException
     */
    public static Map<String,String> getTradeRefundRequestParam(
            String appId,
            String buyerId,
            String buyerOpenId,
            String subject,
            String outTradeNo,
            String requestNo,
            BigDecimal totalAmount,
            String privateKey,
            String certPath,
            String alipayPublicCertPath,
            String rootCertPath
    ) throws AlipayApiException{
        AlipayTradeRefundRequest refundRequest=new AlipayTradeRefundRequest();
        // refundRequest.setNotifyUrl(notifyUrl);
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo);
        bizContent.put("out_request_no", requestNo);
        bizContent.put("refund_amount", totalAmount.doubleValue());
        bizContent.put("subject", subject);
        if(StrUtil.isNotBlank(buyerId)) {
            bizContent.put("buyer_id", buyerId);
        } else {
            bizContent.put("buyer_open_id", buyerOpenId);
        }
        bizContent.put("timeout_express", "60m");
        refundRequest.setBizContent(bizContent.toJSONString());
        AlipayTradeRefundResponse refundResponse = getAlipayClient(appId, privateKey, certPath, alipayPublicCertPath, rootCertPath).certificateExecute(refundRequest);
        if(!refundResponse.isSuccess()){
            log.error("支付宝退款失败={}", refundResponse.getBody());
            throw new RuntimeException("支付宝退款失败:"+ refundResponse.getBody());
        }
        if(!SUCCESS_CODE.equals(refundResponse.getCode())){
            log.error("唤起支付退款-验签失败={}", refundResponse.getBody());
            throw new RuntimeException("唤起支付退款-验签失败 code:"+refundResponse.getBody());
        }
        Map map = JSON.parseObject(refundResponse.getBody(), Map.class);
        log.info("支付宝退款响应成功参数-唤起支付退款={}", map);
        return getMapValueToMap(map, REFUND);
    }

    private static Map getMapValueToMap(Map map, String key) {
        return JSON.parseObject(String.valueOf(map.get(key)), Map.class);
    }

    public static Map<String, String> getRequestParam(HttpServletRequest request, String alipayPublicCertPath) {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        log.info("支付宝回调，{}", paramsJson);
        try {
//            // 调用SDK验证签名 有问题，暂时先不验证
//            boolean signVerified = AlipaySignature.certVerifyV1(params, alipayPublicCertPath, CHARSET, SIGN_TYPE);
//            if (!signVerified) {
//                log.error("支付宝回调验签失败");
//                return new HashMap<>();
//            }
            return params;
        } catch (Exception e) {
            log.error("支付宝回调签名认证失败", e);
            return new HashMap<>();
        }
    }

    public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }

    /**
     * 获取客户端
     */
    public static AlipayClient getAlipayClient(String appId, String privateKey, String certPath, String alipayPublicCertPath, String rootCertPath) throws AlipayApiException {
        //构造client
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //设置网关地址
        certAlipayRequest.setServerUrl(SERVER_URL);
        //设置应用Id
        certAlipayRequest.setAppId(appId);
        //设置应用私钥
        certAlipayRequest.setPrivateKey(privateKey);
        //设置请求格式，固定值json
        certAlipayRequest.setFormat(FORMAT);
        //设置字符集
        certAlipayRequest.setCharset(CHARSET);
        //设置签名类型
        certAlipayRequest.setSignType(SIGN_TYPE);
        //设置应用公钥证书路径
        certAlipayRequest.setCertPath(certPath);
        //设置支付宝公钥证书路径
        certAlipayRequest.setAlipayPublicCertPath(alipayPublicCertPath);
        //设置支付宝根证书路径
        certAlipayRequest.setRootCertPath(rootCertPath);
        return new DefaultAlipayClient(certAlipayRequest);
    }
}
